package cn.lg.soar.flow.biz.service.impl;

import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.ParameterUtil;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.database.base.service.impl.BaseServiceImpl;
import cn.lg.soar.flow.api.enums.ApproverTypeEnum;
import cn.lg.soar.flow.api.model.node.ApprovalNode;
import cn.lg.soar.flow.api.model.node.Assignee;
import cn.lg.soar.flow.api.model.node.Node;
import cn.lg.soar.flow.api.model.node.ProcessListener;
import cn.lg.soar.flow.api.utils.ProcessUtils;
import cn.lg.soar.flow.api.utils.XmlUtils;
import cn.lg.soar.flow.biz.entity.SoarProcess;
import cn.lg.soar.flow.biz.entity.SoarProcessVersion;
import cn.lg.soar.flow.biz.mapper.SoarProcessMapper;
import cn.lg.soar.flow.biz.service.SoarProcessService;
import cn.lg.soar.flow.biz.service.SoarProcessVersionService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 流程定义
 * @author luguoxiang
 * @since 2025-08-02
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Slf4j
@Service
public class SoarProcessServiceImpl extends BaseServiceImpl<SoarProcessMapper, SoarProcess> implements SoarProcessService {

    @Autowired
    private SoarProcessVersionService soarProcessVersionService;
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public SoarProcess getByCode(String flowCode) {
        return lambdaQuery().eq(SoarProcess::getCode, flowCode).one();
    }

    @Override
    public void deploy(Long soarProcessId) {
        SoarProcess soarProcess = getById(soarProcessId);
        AssertUtil.notNull(soarProcess, "流程不存在");
        List<Node> nodes = soarProcess.getNodes();
        AssertUtil.notEmpty(nodes, "请先设计流程");
        // 校验审批人
        List<ApprovalNode> approvalNodes = ProcessUtils.getApprovalNodes(nodes);
        for (ApprovalNode node : approvalNodes) {
            String nodeName = node.getName();
            List<Assignee> assignees = node.getAssignees();
            for (Assignee assignee : assignees) {
                ApproverTypeEnum type = assignee.getType();
                switch (type) {
                    case SPECIFIED_USER -> {
                        ParameterUtil.notNull(assignee.getIds(), "节点“{0}”请指定用户", nodeName);
                    }
                    case SPECIFIED_POST -> {
                        ParameterUtil.notNull(assignee.getIds(), "节点“{0}”请指定职位", nodeName);
                    }
                    case SPECIFIED_ROLE, INITIATOR_DEPT_ROLE, BUSINESS_DEPT_ROLE -> {
                        if (assignee.getIntValue() == null) {
                            assignee.setIntValue(1);
                        }
                        ParameterUtil.notNull(assignee.getLongValue(), "节点“{0}”请指定角色", nodeName);
                    }
                    case FORM_USER, FORM_POST -> {
                        ParameterUtil.notNull(assignee.getStrValue(), "节点“{0}”请指定表单字段", nodeName);
                    }
                    case INITIATOR_DEPT_POST, BUSINESS_DEPT_POST -> {
                        ParameterUtil.notNull(assignee.getStrValue(), "节点“{0}”请指定岗位标签", nodeName);
                    }
                    default -> {
                        
                    }
                }
            }
        }
        // 部署流程
        String bpmnXml = toBpmnXml(soarProcess);
        Deployment deployment = deployBpmnXml(bpmnXml, soarProcess.getName());
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        String definitionId = processDefinition.getId();
        // 保存版本配置
        SoarProcessVersion version = new SoarProcessVersion();
        version.setId(definitionId);
        version.setName(soarProcess.getName());
        version.setTitleTemplate(soarProcess.getTitleTemplate());
        version.setFields(soarProcess.getFields());
        version.setButtons(soarProcess.getButtons());
        version.setListeners(soarProcess.getListeners());
        version.setNodes(nodes);
        AssertUtil.isTrue(soarProcessVersionService.save(version), "操作失败");
        // 更新流程定义
        soarProcess = new SoarProcess();
        soarProcess.setId(soarProcessId);
        soarProcess.setVersionId(definitionId);
        soarProcess.setLastDeployTime(LocalDateTime.now());
        AssertUtil.isTrue(updateById(soarProcess), "操作失败");;
    }

    /**
     * 部署BPMN XML字符串为一个新的流程定义
     *
     * @param bpmnXmlContent BPMN XML格式的字符串
     * @param deploymentName 部署名称
     */
    public Deployment deployBpmnXml(String bpmnXmlContent, String deploymentName) {
        log.info("署流程定义: {}", bpmnXmlContent);
        Deployment deployment = repositoryService.createDeployment()
                .addString("process.bpmn20.xml", bpmnXmlContent) // 给定资源名称和内容
                .name(deploymentName) // 设置部署名称
                .deploy(); // 执行部署操作
        log.info("已部署流程定义: {}", deployment.getId());
        return deployment;
    }

    private String toBpmnXml(SoarProcess process) {
        String code = process.getCode();
        String name = process.getName();
        List<Node> nodes = process.getNodes();
        StringBuilder sb = new StringBuilder(MessageFormat.format("""
                <?xml version="1.0" encoding="UTF-8"?>
                <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
                             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                             xmlns:flowable="http://flowable.org/bpmn"
                             targetNamespace="http://flowable.org/bpmn2.0">
                
                  <process id="{0}" name="{1}" isExecutable="true">
                """, code, name));
        List<ProcessListener> listeners = process.getListeners();
        if (DataUtil.isValuable(listeners)) {
            sb.append("<extensionElements>\n");
            for (ProcessListener x : listeners) {
                sb.append("<flowable:executionListener event=\"").append(x.getEvent()).append("\" ")
                        .append(Boolean.TRUE.equals(x.getIsExpression()) ? "expression" : "class")
                        .append("=\"").append(XmlUtils.escapeXml(x.getValue())).append("\" />\n");
            }
            sb.append("</extensionElements>\n");
        }
        sb.append("<startEvent id=\"startEvent\" name=\"开始\"/>");

        String sourceRef = "startEvent";
        for (Node node : nodes) {
            sb.append("<sequenceFlow id=\"").append(node.getKey())
                    .append("_in_flow\" sourceRef=\"").append(sourceRef)
                    .append("\" targetRef=\"").append(node.inKey()).append("\" />\n");
            String nodeXml = node.toXmlString();
            sb.append(nodeXml);
            // 记录当前节点
            sourceRef = node.outKey();
        }
        sb.append("""
                </process>
                </definitions>
                """);
        return sb.toString();
    }

}
